package com.swak.fx.support.ui;

import java.util.ArrayList;
import java.util.List;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;

public class UILink extends Control implements UILifecycle {

	// ================================================================================
	// Properties
	// ================================================================================
	private final String STYLE_CLASS = "ui-link";
	private final String STYLESHEET = UI.load("css/UILink.css");

	public UILink() {
		super();
		init();
	}

	public UILink(String text) {
		super();
		init();
		this.textProperty().set(text);
	}

	@Override
	public void initializeParts() {
		getStyleClass().add(STYLE_CLASS);
	}

	@Override
	public void layoutParts() {
	}

	@Override
	public void setupValueChangedListeners() {
	}

	private final StringProperty text = new SimpleStringProperty(this, "text"); //$NON-NLS-1$

	public final StringProperty textProperty() {
		return text;
	}

	public final String getText() {
		return text.get();
	}

	public final void setText(String value) {
		text.set(value);
	}

	private ObjectProperty<EventHandler<ActionEvent>> onAction;

	public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() {
		if (onAction == null) {
			onAction = new SimpleObjectProperty<EventHandler<ActionEvent>>(this, "onAction") { //$NON-NLS-1$
				@Override
				protected void invalidated() {
					setEventHandler(ActionEvent.ACTION, get());
				}
			};
		}
		return onAction;
	}

	public final void setOnAction(EventHandler<ActionEvent> value) {
		onActionProperty().set(value);
	}

	public final EventHandler<ActionEvent> getOnAction() {
		return onAction == null ? null : onAction.get();
	}

	// ================================================================================
	// Overridden Methods
	// ================================================================================

	@Override
	protected Skin<?> createDefaultSkin() {
		return new UILinkSkin(this);
	}

	@Override
	public String getUserAgentStylesheet() {
		return STYLESHEET;
	}

	private static class UILinkSkin extends SkinBase<UILink> {

		// The strings used to delimit the hyperlinks
		private static final String HYPERLINK_START = "["; //$NON-NLS-1$
		private static final String HYPERLINK_END = "]"; //$NON-NLS-1$

		private final TextFlow textFlow;
		private final EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {
			@Override
			public void handle(final ActionEvent event) {
				EventHandler<ActionEvent> onActionHandler = getSkinnable().getOnAction();
				if (onActionHandler != null) {
					onActionHandler.handle(event);
				}
			}
		};

		protected UILinkSkin(UILink control) {
			super(control);

			this.textFlow = new TextFlow();
			getChildren().add(textFlow);
			updateText();

			registerChangeListener(control.textProperty(), e -> updateText()); // $NON-NLS-1$
		}

		// splits up the string into Text and Hyperlink nodes, and places them
		// into a TextFlow instance
		private void updateText() {
			final String text = getSkinnable().getText();

			if (text == null || text.isEmpty()) {
				textFlow.getChildren().clear();
				return;
			}

			// parse the text and put it into an array list
			final List<Node> nodes = new ArrayList<>();

			int start = 0;
			final int textLength = text.length();
			while (start != -1 && start < textLength) {
				int startPos = text.indexOf(HYPERLINK_START, start);
				int endPos = text.indexOf(HYPERLINK_END, startPos);

				// if the startPos is -1, there are no more hyperlinks...
				if (startPos == -1 || endPos == -1) {
					if (textLength > start) {
						// ...but there is still text to turn into one last label
						Label label = new Label(text.substring(start));
						nodes.add(label);
						break;
					}
				}

				// firstly, create a label from start to startPos
				Text label = new Text(text.substring(start, startPos));
				nodes.add(label);

				// if endPos is greater than startPos, create a hyperlink
				Hyperlink hyperlink = new Hyperlink(text.substring(startPos + 1, endPos));
				hyperlink.setPadding(new Insets(0, 0, 0, 0));
				hyperlink.setOnAction(eventHandler);
				nodes.add(hyperlink);

				start = endPos + 1;
			}

			textFlow.getChildren().setAll(nodes);
		}

	}
}
